import hashlib

class User:
    def __init__(self, username, password):
        '''Create a new user object. The password
        will be encrypted before storing.'''
        self.username = username
        self.password = password
        self.is_logged_in = False

    # def _encrypt_pw(self, password):
    #     '''Encrypt the password with the username and return
    #     the sha digest.'''
    #     hash_string = (self.username + password)
    #     hash_string = hash_string.encode("utf8")
    #     return hashlib.sha256(hash_string).hexdigest()

    def check_password(self, password):
        '''Return True if the password is valid for this
        user, false otherwise.'''
        # encrypted = self._encrypt_pw(password)
        return password == self.password

class AuthException(Exception):
    def __init__(self, username, user=None):
        super().__init__(username)
        self.username = username
        self.user = user

class UsernameAlreadyExists(AuthException):
    pass

class PasswordTooShort(AuthException):
    pass

class InvalidUsername(AuthException):
    pass

class InvalidPassword(AuthException):
    pass

class PermissionError(Exception):
    pass

class NotLoggedInError(AuthException):
    pass

class NotPermittedError(AuthException):
    pass

class Authenticator:       # 身份验证器或认证器
    def __init__(self):
        '''Construct an authenticator to manage
        users logging in and out.'''
        self.users = {}       # 这里的users是一个字典，用来存储用户名和对应的User对象

    def add_user(self, username, password):
        if username in self.users:
            raise UsernameAlreadyExists(username)
        if len(password) < 6:
            raise PasswordTooShort(username)
        self.users[username] = User(username, password)

    def login(self, username, password):
        try:
            user = self.users[username]
        except KeyError:
            raise InvalidUsername(username)  
        # 这里可以用 if username not in self.users: 来处理，但是我们选择直接处理这个异常，
        # 我们捕获这个异常，并抛出一个我们自己定义的、更符合面向用户API的新异常

        if not user.check_password(password):
            raise InvalidPassword(username, user)   # 同上

        user.is_logged_in = True
        return True

    def is_logged_in(self, username):
        if username in self.users:
            return self.users[username].is_logged_in
        return False

class Authorizor:               # 授权器或授权器
    def __init__(self, authenticator):
        self.authenticator = authenticator
        self.permissions = {}

    def add_permission(self, perm_name):
        '''Create a new permission that users
        can be added to'''
        try:
            perm_set = self.permissions[perm_name]
        except KeyError:
            self.permissions[perm_name] = set()
        else:
            raise PermissionError("Permission Exists")

    def permit_user(self, perm_name, username):
        '''Grant the given permission to the user'''
        try:
            perm_set = self.permissions[perm_name]
        except KeyError:
            raise PermissionError("Permission does not exist")
        else:
            if username not in self.authenticator.users:
                raise InvalidUsername(username)
            perm_set.add(username)

    def check_permission(self, perm_name, username):
        if not self.authenticator.is_logged_in(username):
            raise NotLoggedInError(username)
        try:
            perm_set = self.permissions[perm_name]
        except KeyError:
            raise PermissionError("Permission does not exist")
        else:
            if username not in perm_set:
                raise NotPermittedError(username)
            else:
                return True



authenticator = Authenticator()
authorizor = Authorizor(authenticator)


if __name__ == '__main__':
    # Add some users
    authenticator.add_user("user1", "password1")
    authenticator.add_user("user2", "password2")

    try:
        authenticator.login("user3", "password")
    except InvalidUsername as e:
        print("无效的用户名:", e.username)
    except InvalidPassword as e:
        print("无效的密码:", e.username)

